Get the newest 5 articles out of hundreds -- a super efficient sort in XSLT

By  Dimitre Novatchev
 
Language  XPath
Category designpatterns, xml, msxml
Posted 08  Mar  2001
Updated 08  Mar  2001
 
Summary
Imagine you're developing an XSLT debugger and need some way of finding and displaying all "xsl:variable"-s that are "visible" from the current node. Is it at all possible to specify them using a single XPath expression? Read further to find out...
 
This is a real problem and hereÆs how it was specified by its author in the xslt mailing list:
 
ôWell it seems I've hit a small snag what I want is to get a list of variables ("P"-s) in a scope so the closest is taken and passed on. The XML looks something like:
 
<A>
  <P name="hello" value="blue" />
  <P name="aval" value="uppest" />
  <B>
    <P name="bval" value="upper" />
    <P name="goodbye" value="red" />
    <P name="hello" value="green" />
    <C>
      <P name="goodbye" value="yellow" />
    </C>
    <P name="abcdef" value="orange" />
    <xxx>
      <P name="goodbye" value="purple" />
    </xxx>
  </B>
  <D>
    <P name="goodbye" value="red" />
  </D>
</A>
And my current context is C. With my output looking something like
 
goodbye=yellow;hello=green;bval=upper;aval=uppest;
 
So far I've got
 
<xsl:for-each select="ancestor-or-self::*/p[ what on earth can I put here ]">
   <xsl:value-of select="@name"/>=<xsl:value-of select="@value"/>;
</xsl:for-each>
But I've got stuck in the way that MSXML3 seems to be serving up the axis in document order not reverse document order like it says on P717 or Michael Kays excellent book.
 
Any and all help would be appreciated.ö
 
A number of persons (actually some of the best experts) sent long XSLT code or said some kind of recursive processing had to be used.
 
LetÆs now concentrate on a pure XPath solution.
 
It would be useful to have the XPath Visualiser already started (never heard of the XPath Visualiser? Hmmmà now I understand why XPath is so difficult to youà :o) Just give it a try and youÆll know what I mean) and to verify that the sub-expressions weÆd be building incrementally really behave as weÆd expected them to.
 
First of all, what does ôin scopeö mean?
 
1. Well, any ô thatÆs defined as a child of ôCö-s parent or as a child of any of ôCö-s ancestor nodes.
 
Why ancestor ônodeö and not just ôelement? Because the root node ô/ö is a node û not an element ûweÆd like not to exclude any of the top-level <P>s.
 
Do you agree with the definition in 1.? Hmmmà not entirely û ôCö will not be able to see any ôPö children of its ancestors that are defined (follow) after ôCö.
 
Therefore:
 
2. Exclude from 1. all nodes following ôCö
 
Now the most important requirement: in case there are several ôPö-s with the same ônameö attribute, we want just the innermost of these:
 
3. Take from (1. and 2.) only such nodes, whose ônameö is not duplicated in any of their descendents.
 
Now we are ready to construct the whole XPath expression:
 
For 1. weÆll have:
 //P[parent::C
or
 count(.. | //C/ancestor::node())=count(//C/ancestor::node() )]
 
For 3. we have:
 
and not(@name=../descendant::node()/P/@name )
 
For 2. we have:
 
and count(. | //C/following::P) != count(//C/following::P )
 
So, letÆs combine these together:
 
//P[parent::C
or count(.. | //C/ancestor::node()) = count(//C/ancestor::node() )
and not(@name=../descendant::node()/P/@name )
and count(. | //C/following::P) != count(//C/following::P )]
 
Here IÆm using the following to specify the fact that a node nd belongs to a node-set ns:
 
count(nd | ns) = count(ns)
 
and, similarly, to specify that nd does not belong to ns:
 
count(nd | ns) != count(ns)
 
In case you find it difficult to understand the last two expressions, do have a look at another snippet that explains the Kaysian method of intersecting two node-sets.
 
A last remark: WeÆre using ô//Cö here û just because weÆre solving a particular example problem. For the general case simply replace this with ôcurrent()ö.
 
Code